package com.example.demo.utils;

import com.example.demo.plc.S7ConnectorPLC;
import com.example.demo.pojo.PlcData;
import com.github.s7connector.api.DaveArea;
import com.github.s7connector.api.S7Connector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * 读取PLC数据（使用ValueCall工具类解析字节数据）
 */
public class PlcRead {
    //添加log4j2的日志
    private static final Logger logger= LogManager.getLogger(PlcRead.class);
    //读取状态
    int status=0;
    private static final int DBblock = 6;
    // 复用PLC连接（避免每次读取重新创建连接）
    private static S7Connector connector;

    // 初始化连接（只执行一次）
    static {
        try {
            connector = S7ConnectorPLC.initConnect("192.168.2.2");
        } catch (Exception e) {
            logger.error("PLC连接初始化失败: " + e.getMessage());
//            System.err.println("PLC连接初始化失败: " + e.getMessage());
        }
    }

    /**
     * 读取plc数据
     * @return 读取的整个数据对象
     */
    public PlcData PlcOpc_read() {
        if (connector == null) {
            logger.error("PLC连接未初始化，无法读取数据");
//            System.err.println("PLC连接未初始化，无法读取数据");
            return null;
        }
        //创建数据接收对象
        PlcData plcData = new PlcData();
        Map<String, Object> valueMap = new LinkedHashMap<>();
        List<String> names = getFieldNames(PlcData.class);
        try {
            //读取plc的开始时间
            long plcReadStart = System.currentTimeMillis();
            // 批量读取DB6中偏移量0到40的数据（长度41字节）
            byte[] allData = connector.read(DaveArea.DB, DBblock, 41, 0);
            //结束时间
            long plcReadTime = System.currentTimeMillis() - plcReadStart;
            logger.info("批量读取耗时: " + plcReadTime + "ms");
//            System.out.println("批量读取耗时: " + plcReadTime + "ms");

            int num = 0;

            // 1. 解析布尔值：DB6.0.0~2.7（通过ValueCall工具类）
            for (int byteOffset = 0; byteOffset <= 2; byteOffset++) {
                for (int bit = 0; bit <= 7; bit++) {
                    boolean value = ValueCall.aBoolean(allData, byteOffset, bit);
                    valueMap.put(names.get(num), value);
                    num++;
                }
            }

            // 2. 解析DB6.3.0（字节偏移3，位0）
            boolean b3 = ValueCall.aBoolean(allData, 3, 0);
            valueMap.put(names.get(num), b3);
            num++;

            // 3. 解析整数（DB6.4.0开始，通过ValueCall工具类）
            int i4 = ValueCall.aint(allData, 4);
            valueMap.put(names.get(num), i4);
            num++;

            int i6 = ValueCall.aint(allData, 6);
            valueMap.put(names.get(num), i6);
            num++;

            int i8 = ValueCall.aint(allData, 8);
            valueMap.put(names.get(num), i8);
            num++;

            // 继续解析其他整数
            int[] intOffsets = {10, 14, 18};
            for (int offset : intOffsets) {
                long value = ValueCall.adint(allData, offset);
                valueMap.put(names.get(num), value);
                num++;
            }

            // 4. 解析DB6.22.0~22.4
            for (int bit = 0; bit < 5; bit++) {
                boolean b22 = ValueCall.aBoolean(allData, 22, bit);
                valueMap.put(names.get(num), b22);
                num++;
            }

            // 5. 解析DB6.24~40的整数（共8个）
            for (int j = 0; j < 7; j++) {
                int offset = 24 + j * 2;
                int value = ValueCall.aint(allData, offset);
                valueMap.put(names.get(num), value);
                num++;
            }

            // 设置属性值
            Reflection.setValuesInOrder(plcData, valueMap);

        } catch (Exception e) {
            logger.error("PLC批量读取/解析失败: " + e.getMessage());
//            System.err.println("PLC批量读取/解析失败: " + e.getMessage());
            throw new RuntimeException("PLC批量读取/解析失败");
        }
        status=1;
        plcData.setReadStatus(status);
        return plcData;
    }

    // 提取PlcData的字段名（缓存结果）
    private List<String> getFieldNames(Class<PlcData> clazz) {
        List<String> names = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            names.add(field.getName());
        }
        return names;
    }
}